/*
 * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.nulldev.util.graphics.renderIt.ginterfaces.marlin.impl.geom;

import java.awt.Shape;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.IllegalPathStateException;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import sun.awt.geom.Curve;
import java.io.Serializable;
import java.io.StreamCorruptedException;
import java.util.Arrays;

/**
 * The {@code Path2D} class provides a simple, yet flexible shape which
 * represents an arbitrary geometric path. It can fully represent any path which
 * can be iterated by the {@link PathIterator} interface including all of its
 * segment types and winding rules and it implements all of the basic hit
 * testing methods of the {@link Shape} interface.
 * <p>
 * Use {@link Path2D.Float} when dealing with data that can be represented and
 * used with floating point precision. Use {@link Path2D.Double} for data that
 * requires the accuracy or range of double precision.
 * <p>
 * {@code Path2D} provides exactly those facilities required for basic
 * construction and management of a geometric path and implementation of the
 * above interfaces with little added interpretation. If it is useful to
 * manipulate the interiors of closed geometric shapes beyond simple hit testing
 * then the {@link Area} class provides additional capabilities specifically
 * targeted at closed figures. While both classes nominally implement the
 * {@code Shape} interface, they differ in purpose and together they provide two
 * useful views of a geometric shape where {@code Path2D} deals primarily with a
 * trajectory formed by path segments and {@code Area} deals more with
 * interpretation and manipulation of enclosed regions of 2D geometric space.
 * <p>
 * The {@link PathIterator} interface has more detailed descriptions of the
 * types of segments that make up a path and the winding rules that control how
 * to determine which regions are inside or outside the path.
 *
 * @author Jim Graham
 * @since 1.6
 */
public abstract class Path2D implements Shape, Cloneable {
	/**
	 * An even-odd winding rule for determining the interior of a path.
	 *
	 * @see PathIterator#WIND_EVEN_ODD
	 * @since 1.6
	 */
	public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;

	/**
	 * A non-zero winding rule for determining the interior of a path.
	 *
	 * @see PathIterator#WIND_NON_ZERO
	 * @since 1.6
	 */
	public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;

	// For code simplicity, copy these constants to our namespace
	// and cast them to byte constants for easy storage.
	private static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO;
	private static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO;
	private static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO;
	private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
	private static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE;

	transient byte[] pointTypes;
	transient int numTypes;
	transient int numCoords;
	transient int windingRule;

	static final int INIT_SIZE = 20;
	static final int EXPAND_MAX = 500;
	static final int EXPAND_MAX_COORDS = EXPAND_MAX * 2;
	static final int EXPAND_MIN = 10; // ensure > 6 (cubics)

	/**
	 * Constructs a new empty {@code Path2D} object. It is assumed that the package
	 * sibling subclass that is defaulting to this constructor will fill in all
	 * values.
	 *
	 * @since 1.6
	 */
	/* private protected */
	Path2D() {
	}

	/**
	 * Constructs a new {@code Path2D} object from the given specified initial
	 * values. This method is only intended for internal use and should not be made
	 * public if the other constructors for this class are ever exposed.
	 *
	 * @param rule         the winding rule
	 * @param initialTypes the size to make the initial array to store the path
	 *                     segment types
	 * @since 1.6
	 */
	/* private protected */
	Path2D(int rule, int initialTypes) {
		setWindingRule(rule);
		this.pointTypes = new byte[initialTypes];
	}

	abstract float[] cloneCoordsFloat(AffineTransform at);

	abstract double[] cloneCoordsDouble(AffineTransform at);

	abstract void append(float x, float y);

	abstract void append(double x, double y);

	abstract Point2D getPoint(int coordindex);

	abstract void needRoom(boolean needMove, int newCoords);

	abstract int pointCrossings(double px, double py);

	abstract int rectCrossings(double rxmin, double rymin, double rxmax, double rymax);

	static byte[] expandPointTypes(byte[] oldPointTypes, int needed) {
		final int oldSize = oldPointTypes.length;
		final int newSizeMin = oldSize + needed;
		if (newSizeMin < oldSize) {
			// hard overflow failure - we can't even accommodate
			// new items without overflowing
			throw new ArrayIndexOutOfBoundsException("pointTypes exceeds maximum capacity !");
		}
		// growth algorithm computation
		int grow = oldSize;
		if (grow > EXPAND_MAX) {
			grow = Math.max(EXPAND_MAX, oldSize >> 3); // 1/8th min
		} else if (grow < EXPAND_MIN) {
			grow = EXPAND_MIN;
		}
		assert grow > 0;

		int newSize = oldSize + grow;
		if (newSize < newSizeMin) {
			// overflow in growth algorithm computation
			newSize = Integer.MAX_VALUE;
		}
		while (true) {
			try {
				// try allocating the larger array
				return Arrays.copyOf(oldPointTypes, newSize);
			} catch (OutOfMemoryError oome) {
				if (newSize == newSizeMin) {
					throw oome;
				}
			}
			newSize = newSizeMin + (newSize - newSizeMin) / 2;
		}
	}

	/**
	 * The {@code Float} class defines a geometric path with coordinates stored in
	 * single precision floating point.
	 *
	 * @since 1.6
	 */
	public static class Float extends Path2D implements Serializable {
		transient float floatCoords[];

		/**
		 * Constructs a new empty single precision {@code Path2D} object with a default
		 * winding rule of {@link #WIND_NON_ZERO}.
		 *
		 * @since 1.6
		 */
		public Float() {
			this(WIND_NON_ZERO, INIT_SIZE);
		}

		/**
		 * Constructs a new empty single precision {@code Path2D} object with the
		 * specified winding rule to control operations that require the interior of the
		 * path to be defined.
		 *
		 * @param rule the winding rule
		 * @see #WIND_EVEN_ODD
		 * @see #WIND_NON_ZERO
		 * @since 1.6
		 */
		public Float(int rule) {
			this(rule, INIT_SIZE);
		}

		/**
		 * Constructs a new empty single precision {@code Path2D} object with the
		 * specified winding rule and the specified initial capacity to store path
		 * segments. This number is an initial guess as to how many path segments will
		 * be added to the path, but the storage is expanded as needed to store whatever
		 * path segments are added.
		 *
		 * @param rule            the winding rule
		 * @param initialCapacity the estimate for the number of path segments in the
		 *                        path
		 * @see #WIND_EVEN_ODD
		 * @see #WIND_NON_ZERO
		 * @since 1.6
		 */
		public Float(int rule, int initialCapacity) {
			super(rule, initialCapacity);
			floatCoords = new float[initialCapacity * 2];
		}

		/**
		 * Constructs a new single precision {@code Path2D} object from an arbitrary
		 * {@link Shape} object. All of the initial geometry and the winding rule for
		 * this path are taken from the specified {@code Shape} object.
		 *
		 * @param s the specified {@code Shape} object
		 * @since 1.6
		 */
		public Float(Shape s) {
			this(s, null);
		}

		/**
		 * Constructs a new single precision {@code Path2D} object from an arbitrary
		 * {@link Shape} object, transformed by an {@link AffineTransform} object. All
		 * of the initial geometry and the winding rule for this path are taken from the
		 * specified {@code Shape} object and transformed by the specified
		 * {@code AffineTransform} object.
		 *
		 * @param s  the specified {@code Shape} object
		 * @param at the specified {@code AffineTransform} object
		 * @since 1.6
		 */
		public Float(Shape s, AffineTransform at) {
			if (s instanceof Path2D) {
				Path2D p2d = (Path2D) s;
				setWindingRule(p2d.windingRule);
				this.numTypes = p2d.numTypes;
				// trim arrays:
				this.pointTypes = Arrays.copyOf(p2d.pointTypes, p2d.numTypes);
				this.numCoords = p2d.numCoords;
				this.floatCoords = p2d.cloneCoordsFloat(at);
			} else {
				PathIterator pi = s.getPathIterator(at);
				setWindingRule(pi.getWindingRule());
				this.pointTypes = new byte[INIT_SIZE];
				this.floatCoords = new float[INIT_SIZE * 2];
				append(pi, false);
			}
		}

		@Override
		float[] cloneCoordsFloat(AffineTransform at) {
			// trim arrays:
			float ret[];
			if (at == null) {
				ret = Arrays.copyOf(floatCoords, numCoords);
			} else {
				ret = new float[numCoords];
				at.transform(floatCoords, 0, ret, 0, numCoords / 2);
			}
			return ret;
		}

		@Override
		double[] cloneCoordsDouble(AffineTransform at) {
			// trim arrays:
			double ret[] = new double[numCoords];
			if (at == null) {
				for (int i = 0; i < numCoords; i++) {
					ret[i] = floatCoords[i];
				}
			} else {
				at.transform(floatCoords, 0, ret, 0, numCoords / 2);
			}
			return ret;
		}

		void append(float x, float y) {
			floatCoords[numCoords++] = x;
			floatCoords[numCoords++] = y;
		}

		void append(double x, double y) {
			floatCoords[numCoords++] = (float) x;
			floatCoords[numCoords++] = (float) y;
		}

		Point2D getPoint(int coordindex) {
			return new Point2D.Float(floatCoords[coordindex], floatCoords[coordindex + 1]);
		}

		@Override
		void needRoom(boolean needMove, int newCoords) {
			if ((numTypes == 0) && needMove) {
				throw new IllegalPathStateException("missing initial moveto " + "in path definition");
			}
			if (numTypes >= pointTypes.length) {
				pointTypes = expandPointTypes(pointTypes, 1);
			}
			if (numCoords > (floatCoords.length - newCoords)) {
				floatCoords = expandCoords(floatCoords, newCoords);
			}
		}

		static float[] expandCoords(float[] oldCoords, int needed) {
			final int oldSize = oldCoords.length;
			final int newSizeMin = oldSize + needed;
			if (newSizeMin < oldSize) {
				// hard overflow failure - we can't even accommodate
				// new items without overflowing
				throw new ArrayIndexOutOfBoundsException("coords exceeds maximum capacity !");
			}
			// growth algorithm computation
			int grow = oldSize;
			if (grow > EXPAND_MAX_COORDS) {
				grow = Math.max(EXPAND_MAX_COORDS, oldSize >> 3); // 1/8th min
			} else if (grow < EXPAND_MIN) {
				grow = EXPAND_MIN;
			}
			assert grow > needed;

			int newSize = oldSize + grow;
			if (newSize < newSizeMin) {
				// overflow in growth algorithm computation
				newSize = Integer.MAX_VALUE;
			}
			while (true) {
				try {
					// try allocating the larger array
					return Arrays.copyOf(oldCoords, newSize);
				} catch (OutOfMemoryError oome) {
					if (newSize == newSizeMin) {
						throw oome;
					}
				}
				newSize = newSizeMin + (newSize - newSizeMin) / 2;
			}
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final synchronized void moveTo(double x, double y) {
			if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
				floatCoords[numCoords - 2] = (float) x;
				floatCoords[numCoords - 1] = (float) y;
			} else {
				needRoom(false, 2);
				pointTypes[numTypes++] = SEG_MOVETO;
				floatCoords[numCoords++] = (float) x;
				floatCoords[numCoords++] = (float) y;
			}
		}

		/**
		 * Adds a point to the path by moving to the specified coordinates specified in
		 * float precision.
		 * <p>
		 * This method provides a single precision variant of the double precision
		 * {@code moveTo()} method on the base {@code Path2D} class.
		 *
		 * @param x the specified X coordinate
		 * @param y the specified Y coordinate
		 * @see Path2D#moveTo
		 * @since 1.6
		 */
		public final synchronized void moveTo(float x, float y) {
			if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
				floatCoords[numCoords - 2] = x;
				floatCoords[numCoords - 1] = y;
			} else {
				needRoom(false, 2);
				pointTypes[numTypes++] = SEG_MOVETO;
				floatCoords[numCoords++] = x;
				floatCoords[numCoords++] = y;
			}
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final synchronized void lineTo(double x, double y) {
			needRoom(true, 2);
			pointTypes[numTypes++] = SEG_LINETO;
			floatCoords[numCoords++] = (float) x;
			floatCoords[numCoords++] = (float) y;
		}

		/**
		 * Adds a point to the path by drawing a straight line from the current
		 * coordinates to the new specified coordinates specified in float precision.
		 * <p>
		 * This method provides a single precision variant of the double precision
		 * {@code lineTo()} method on the base {@code Path2D} class.
		 *
		 * @param x the specified X coordinate
		 * @param y the specified Y coordinate
		 * @see Path2D#lineTo
		 * @since 1.6
		 */
		public final synchronized void lineTo(float x, float y) {
			needRoom(true, 2);
			pointTypes[numTypes++] = SEG_LINETO;
			floatCoords[numCoords++] = x;
			floatCoords[numCoords++] = y;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final synchronized void quadTo(double x1, double y1, double x2, double y2) {
			needRoom(true, 4);
			pointTypes[numTypes++] = SEG_QUADTO;
			floatCoords[numCoords++] = (float) x1;
			floatCoords[numCoords++] = (float) y1;
			floatCoords[numCoords++] = (float) x2;
			floatCoords[numCoords++] = (float) y2;
		}

		/**
		 * Adds a curved segment, defined by two new points, to the path by drawing a
		 * Quadratic curve that intersects both the current coordinates and the
		 * specified coordinates {@code (x2,y2)}, using the specified point
		 * {@code (x1,y1)} as a quadratic parametric control point. All coordinates are
		 * specified in float precision.
		 * <p>
		 * This method provides a single precision variant of the double precision
		 * {@code quadTo()} method on the base {@code Path2D} class.
		 *
		 * @param x1 the X coordinate of the quadratic control point
		 * @param y1 the Y coordinate of the quadratic control point
		 * @param x2 the X coordinate of the final end point
		 * @param y2 the Y coordinate of the final end point
		 * @see Path2D#quadTo
		 * @since 1.6
		 */
		public final synchronized void quadTo(float x1, float y1, float x2, float y2) {
			needRoom(true, 4);
			pointTypes[numTypes++] = SEG_QUADTO;
			floatCoords[numCoords++] = x1;
			floatCoords[numCoords++] = y1;
			floatCoords[numCoords++] = x2;
			floatCoords[numCoords++] = y2;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final synchronized void curveTo(double x1, double y1, double x2, double y2, double x3, double y3) {
			needRoom(true, 6);
			pointTypes[numTypes++] = SEG_CUBICTO;
			floatCoords[numCoords++] = (float) x1;
			floatCoords[numCoords++] = (float) y1;
			floatCoords[numCoords++] = (float) x2;
			floatCoords[numCoords++] = (float) y2;
			floatCoords[numCoords++] = (float) x3;
			floatCoords[numCoords++] = (float) y3;
		}

		/**
		 * Adds a curved segment, defined by three new points, to the path by drawing a
		 * B&eacute;zier curve that intersects both the current coordinates and the
		 * specified coordinates {@code (x3,y3)}, using the specified points
		 * {@code (x1,y1)} and {@code (x2,y2)} as B&eacute;zier control points. All
		 * coordinates are specified in float precision.
		 * <p>
		 * This method provides a single precision variant of the double precision
		 * {@code curveTo()} method on the base {@code Path2D} class.
		 *
		 * @param x1 the X coordinate of the first B&eacute;zier control point
		 * @param y1 the Y coordinate of the first B&eacute;zier control point
		 * @param x2 the X coordinate of the second B&eacute;zier control point
		 * @param y2 the Y coordinate of the second B&eacute;zier control point
		 * @param x3 the X coordinate of the final end point
		 * @param y3 the Y coordinate of the final end point
		 * @see Path2D#curveTo
		 * @since 1.6
		 */
		public final synchronized void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) {
			needRoom(true, 6);
			pointTypes[numTypes++] = SEG_CUBICTO;
			floatCoords[numCoords++] = x1;
			floatCoords[numCoords++] = y1;
			floatCoords[numCoords++] = x2;
			floatCoords[numCoords++] = y2;
			floatCoords[numCoords++] = x3;
			floatCoords[numCoords++] = y3;
		}

		int pointCrossings(double px, double py) {
			if (numTypes == 0) {
				return 0;
			}
			double movx, movy, curx, cury, endx, endy;
			float coords[] = floatCoords;
			curx = movx = coords[0];
			cury = movy = coords[1];
			int crossings = 0;
			int ci = 2;
			for (int i = 1; i < numTypes; i++) {
				switch (pointTypes[i]) {
					case PathIterator.SEG_MOVETO:
						if (cury != movy) {
							crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
						}
						movx = curx = coords[ci++];
						movy = cury = coords[ci++];
						break;
					case PathIterator.SEG_LINETO:
						crossings += Curve.pointCrossingsForLine(px, py, curx, cury, endx = coords[ci++], endy = coords[ci++]);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_QUADTO:
						crossings += Curve.pointCrossingsForQuad(px, py, curx, cury, coords[ci++], coords[ci++], endx = coords[ci++], endy = coords[ci++], 0);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_CUBICTO:
						crossings += Curve.pointCrossingsForCubic(px, py, curx, cury, coords[ci++], coords[ci++], coords[ci++], coords[ci++],
								endx = coords[ci++], endy = coords[ci++], 0);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_CLOSE:
						if (cury != movy) {
							crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
						}
						curx = movx;
						cury = movy;
						break;
				}
			}
			if (cury != movy) {
				crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
			}
			return crossings;
		}

		int rectCrossings(double rxmin, double rymin, double rxmax, double rymax) {
			if (numTypes == 0) {
				return 0;
			}
			float coords[] = floatCoords;
			double curx, cury, movx, movy, endx, endy;
			curx = movx = coords[0];
			cury = movy = coords[1];
			int crossings = 0;
			int ci = 2;
			for (int i = 1; crossings != Curve.RECT_INTERSECTS && i < numTypes; i++) {
				switch (pointTypes[i]) {
					case PathIterator.SEG_MOVETO:
						if (curx != movx || cury != movy) {
							crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
						}
						// Count should always be a multiple of 2 here.
						// assert((crossings & 1) != 0);
						movx = curx = coords[ci++];
						movy = cury = coords[ci++];
						break;
					case PathIterator.SEG_LINETO:
						crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, endx = coords[ci++], endy = coords[ci++]);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_QUADTO:
						crossings = Curve.rectCrossingsForQuad(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[ci++], coords[ci++],
								endx = coords[ci++], endy = coords[ci++], 0);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_CUBICTO:
						crossings = Curve.rectCrossingsForCubic(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[ci++], coords[ci++], coords[ci++],
								coords[ci++], endx = coords[ci++], endy = coords[ci++], 0);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_CLOSE:
						if (curx != movx || cury != movy) {
							crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
						}
						curx = movx;
						cury = movy;
						// Count should always be a multiple of 2 here.
						// assert((crossings & 1) != 0);
						break;
				}
			}
			if (crossings != Curve.RECT_INTERSECTS && (curx != movx || cury != movy)) {
				crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
			}
			// Count should always be a multiple of 2 here.
			// assert((crossings & 1) != 0);
			return crossings;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final void append(PathIterator pi, boolean connect) {
			float coords[] = new float[6];
			while (!pi.isDone()) {
				switch (pi.currentSegment(coords)) {
					case SEG_MOVETO:
						if (!connect || numTypes < 1 || numCoords < 1) {
							moveTo(coords[0], coords[1]);
							break;
						}
						if (pointTypes[numTypes - 1] != SEG_CLOSE && floatCoords[numCoords - 2] == coords[0] && floatCoords[numCoords - 1] == coords[1]) {
							// Collapse out initial moveto/lineto
							break;
						}
						lineTo(coords[0], coords[1]);
						break;
					case SEG_LINETO:
						lineTo(coords[0], coords[1]);
						break;
					case SEG_QUADTO:
						quadTo(coords[0], coords[1], coords[2], coords[3]);
						break;
					case SEG_CUBICTO:
						curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
						break;
					case SEG_CLOSE:
						closePath();
						break;
				}
				pi.next();
				connect = false;
			}
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final void transform(AffineTransform at) {
			at.transform(floatCoords, 0, floatCoords, 0, numCoords / 2);
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final synchronized Rectangle2D getBounds2D() {
			float x1, y1, x2, y2;
			int i = numCoords;
			if (i > 0) {
				y1 = y2 = floatCoords[--i];
				x1 = x2 = floatCoords[--i];
				while (i > 0) {
					float y = floatCoords[--i];
					float x = floatCoords[--i];
					if (x < x1)
						x1 = x;
					if (y < y1)
						y1 = y;
					if (x > x2)
						x2 = x;
					if (y > y2)
						y2 = y;
				}
			} else {
				x1 = y1 = x2 = y2 = 0.0f;
			}
			return new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1);
		}

		/**
		 * {@inheritDoc}
		 * <p>
		 * The iterator for this class is not multi-threaded safe, which means that the
		 * {@code Path2D} class does not guarantee that modifications to the geometry of
		 * this {@code Path2D} object do not affect any iterations of that geometry that
		 * are already in process.
		 *
		 * @since 1.6
		 */
		public final PathIterator getPathIterator(AffineTransform at) {
			if (at == null) {
				return new CopyIterator(this);
			} else {
				return new TxIterator(this, at);
			}
		}

		/**
		 * Creates a new object of the same class as this object.
		 *
		 * @return a clone of this instance.
		 * @exception OutOfMemoryError if there is not enough memory.
		 * @see java.lang.Cloneable
		 * @since 1.6
		 */
		public final Object clone() {
			// Note: It would be nice to have this return Path2D
			// but one of our subclasses (GeneralPath) needs to
			// offer "public Object clone()" for backwards
			// compatibility so we cannot restrict it further.
			// REMIND: Can we do both somehow?
// LBO: Fix class incompatibility:
			/*
			 * if (this instanceof GeneralPath) { return new GeneralPath(this); } else {
			 */
			return new Path2D.Float(this);
//            }
		}

		/*
		 * JDK 1.6 serialVersionUID
		 */
		private static final long serialVersionUID = 6990832515060788886L;

		/**
		 * Writes the default serializable fields to the {@code ObjectOutputStream}
		 * followed by an explicit serialization of the path segments stored in this
		 * path.
		 *
		 * @serialData <a name="Path2DSerialData"><!-- --></a>
		 *             <ol>
		 *             <li>The default serializable fields. There are no default
		 *             serializable fields as of 1.6.
		 *             <li>followed by a byte indicating the storage type of the
		 *             original object as a hint (SERIAL_STORAGE_FLT_ARRAY)
		 *             <li>followed by an integer indicating the number of path segments
		 *             to follow (NP) or -1 to indicate an unknown number of path
		 *             segments follows
		 *             <li>followed by an integer indicating the total number of
		 *             coordinates to follow (NC) or -1 to indicate an unknown number of
		 *             coordinates follows (NC should always be even since coordinates
		 *             always appear in pairs representing an x,y pair)
		 *             <li>followed by a byte indicating the winding rule
		 *             ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or {@link #WIND_NON_ZERO
		 *             WIND_NON_ZERO})
		 *             <li>followed by {@code NP} (or unlimited if {@code NP < 0}) sets
		 *             of values consisting of a single byte indicating a path segment
		 *             type followed by one or more pairs of float or double values
		 *             representing the coordinates of the path segment
		 *             <li>followed by a byte indicating the end of the path
		 *             (SERIAL_PATH_END).
		 *             </ol>
		 *             <p>
		 *             The following byte value constants are used in the serialized
		 *             form of {@code Path2D} objects:
		 *             <table>
		 *             <tr>
		 *             <th>Constant Name</th>
		 *             <th>Byte Value</th>
		 *             <th>Followed by</th>
		 *             <th>Description</th>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
		 *             <td>0x30</td>
		 *             <td></td>
		 *             <td>A hint that the original {@code Path2D} object stored the
		 *             coordinates in a Java array of floats.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
		 *             <td>0x31</td>
		 *             <td></td>
		 *             <td>A hint that the original {@code Path2D} object stored the
		 *             coordinates in a Java array of doubles.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
		 *             <td>0x40</td>
		 *             <td>2 floats</td>
		 *             <td>A {@link #moveTo moveTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_FLT_LINETO}</td>
		 *             <td>0x41</td>
		 *             <td>2 floats</td>
		 *             <td>A {@link #lineTo lineTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
		 *             <td>0x42</td>
		 *             <td>4 floats</td>
		 *             <td>A {@link #quadTo quadTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
		 *             <td>0x43</td>
		 *             <td>6 floats</td>
		 *             <td>A {@link #curveTo curveTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
		 *             <td>0x50</td>
		 *             <td>2 doubles</td>
		 *             <td>A {@link #moveTo moveTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_DBL_LINETO}</td>
		 *             <td>0x51</td>
		 *             <td>2 doubles</td>
		 *             <td>A {@link #lineTo lineTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
		 *             <td>0x52</td>
		 *             <td>4 doubles</td>
		 *             <td>A {@link #curveTo curveTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
		 *             <td>0x53</td>
		 *             <td>6 doubles</td>
		 *             <td>A {@link #curveTo curveTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_CLOSE}</td>
		 *             <td>0x60</td>
		 *             <td></td>
		 *             <td>A {@link #closePath closePath} path segment.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_PATH_END}</td>
		 *             <td>0x61</td>
		 *             <td></td>
		 *             <td>There are no more path segments following.</td>
		 *             </table>
		 *
		 * @since 1.6
		 */
		private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
			super.writeObject(s, false);
		}

		/**
		 * Reads the default serializable fields from the {@code ObjectInputStream}
		 * followed by an explicit serialization of the path segments stored in this
		 * path.
		 * <p>
		 * There are no default serializable fields as of 1.6.
		 * <p>
		 * The serial data for this object is described in the writeObject method.
		 *
		 * @since 1.6
		 */
		private void readObject(java.io.ObjectInputStream s) throws java.lang.ClassNotFoundException, java.io.IOException {
			super.readObject(s, false);
		}

		static class CopyIterator extends Path2D.Iterator {
			float floatCoords[];

			CopyIterator(Path2D.Float p2df) {
				super(p2df);
				this.floatCoords = p2df.floatCoords;
			}

			public int currentSegment(float[] coords) {
				int type = path.pointTypes[typeIdx];
				int numCoords = curvecoords[type];
				if (numCoords > 0) {
					System.arraycopy(floatCoords, pointIdx, coords, 0, numCoords);
				}
				return type;
			}

			public int currentSegment(double[] coords) {
				int type = path.pointTypes[typeIdx];
				int numCoords = curvecoords[type];
				if (numCoords > 0) {
					for (int i = 0; i < numCoords; i++) {
						coords[i] = floatCoords[pointIdx + i];
					}
				}
				return type;
			}
		}

		static class TxIterator extends Path2D.Iterator {
			float floatCoords[];
			AffineTransform affine;

			TxIterator(Path2D.Float p2df, AffineTransform at) {
				super(p2df);
				this.floatCoords = p2df.floatCoords;
				this.affine = at;
			}

			public int currentSegment(float[] coords) {
				int type = path.pointTypes[typeIdx];
				int numCoords = curvecoords[type];
				if (numCoords > 0) {
					affine.transform(floatCoords, pointIdx, coords, 0, numCoords / 2);
				}
				return type;
			}

			public int currentSegment(double[] coords) {
				int type = path.pointTypes[typeIdx];
				int numCoords = curvecoords[type];
				if (numCoords > 0) {
					affine.transform(floatCoords, pointIdx, coords, 0, numCoords / 2);
				}
				return type;
			}
		}

	}

	/**
	 * The {@code Double} class defines a geometric path with coordinates stored in
	 * double precision floating point.
	 *
	 * @since 1.6
	 */
	public static class Double extends Path2D implements Serializable {
		transient double doubleCoords[];

		/**
		 * Constructs a new empty double precision {@code Path2D} object with a default
		 * winding rule of {@link #WIND_NON_ZERO}.
		 *
		 * @since 1.6
		 */
		public Double() {
			this(WIND_NON_ZERO, INIT_SIZE);
		}

		/**
		 * Constructs a new empty double precision {@code Path2D} object with the
		 * specified winding rule to control operations that require the interior of the
		 * path to be defined.
		 *
		 * @param rule the winding rule
		 * @see #WIND_EVEN_ODD
		 * @see #WIND_NON_ZERO
		 * @since 1.6
		 */
		public Double(int rule) {
			this(rule, INIT_SIZE);
		}

		/**
		 * Constructs a new empty double precision {@code Path2D} object with the
		 * specified winding rule and the specified initial capacity to store path
		 * segments. This number is an initial guess as to how many path segments are in
		 * the path, but the storage is expanded as needed to store whatever path
		 * segments are added to this path.
		 *
		 * @param rule            the winding rule
		 * @param initialCapacity the estimate for the number of path segments in the
		 *                        path
		 * @see #WIND_EVEN_ODD
		 * @see #WIND_NON_ZERO
		 * @since 1.6
		 */
		public Double(int rule, int initialCapacity) {
			super(rule, initialCapacity);
			doubleCoords = new double[initialCapacity * 2];
		}

		/**
		 * Constructs a new double precision {@code Path2D} object from an arbitrary
		 * {@link Shape} object. All of the initial geometry and the winding rule for
		 * this path are taken from the specified {@code Shape} object.
		 *
		 * @param s the specified {@code Shape} object
		 * @since 1.6
		 */
		public Double(Shape s) {
			this(s, null);
		}

		/**
		 * Constructs a new double precision {@code Path2D} object from an arbitrary
		 * {@link Shape} object, transformed by an {@link AffineTransform} object. All
		 * of the initial geometry and the winding rule for this path are taken from the
		 * specified {@code Shape} object and transformed by the specified
		 * {@code AffineTransform} object.
		 *
		 * @param s  the specified {@code Shape} object
		 * @param at the specified {@code AffineTransform} object
		 * @since 1.6
		 */
		public Double(Shape s, AffineTransform at) {
			if (s instanceof Path2D) {
				Path2D p2d = (Path2D) s;
				setWindingRule(p2d.windingRule);
				this.numTypes = p2d.numTypes;
				// trim arrays:
				this.pointTypes = Arrays.copyOf(p2d.pointTypes, p2d.numTypes);
				this.numCoords = p2d.numCoords;
				this.doubleCoords = p2d.cloneCoordsDouble(at);
			} else {
				PathIterator pi = s.getPathIterator(at);
				setWindingRule(pi.getWindingRule());
				this.pointTypes = new byte[INIT_SIZE];
				this.doubleCoords = new double[INIT_SIZE * 2];
				append(pi, false);
			}
		}

		@Override
		float[] cloneCoordsFloat(AffineTransform at) {
			// trim arrays:
			float ret[] = new float[numCoords];
			if (at == null) {
				for (int i = 0; i < numCoords; i++) {
					ret[i] = (float) doubleCoords[i];
				}
			} else {
				at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
			}
			return ret;
		}

		@Override
		double[] cloneCoordsDouble(AffineTransform at) {
			// trim arrays:
			double ret[];
			if (at == null) {
				ret = Arrays.copyOf(doubleCoords, numCoords);
			} else {
				ret = new double[numCoords];
				at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
			}
			return ret;
		}

		void append(float x, float y) {
			doubleCoords[numCoords++] = x;
			doubleCoords[numCoords++] = y;
		}

		void append(double x, double y) {
			doubleCoords[numCoords++] = x;
			doubleCoords[numCoords++] = y;
		}

		Point2D getPoint(int coordindex) {
			return new Point2D.Double(doubleCoords[coordindex], doubleCoords[coordindex + 1]);
		}

		@Override
		void needRoom(boolean needMove, int newCoords) {
			if ((numTypes == 0) && needMove) {
				throw new IllegalPathStateException("missing initial moveto " + "in path definition");
			}
			if (numTypes >= pointTypes.length) {
				pointTypes = expandPointTypes(pointTypes, 1);
			}
			if (numCoords > (doubleCoords.length - newCoords)) {
				doubleCoords = expandCoords(doubleCoords, newCoords);
			}
		}

		static double[] expandCoords(double[] oldCoords, int needed) {
			final int oldSize = oldCoords.length;
			final int newSizeMin = oldSize + needed;
			if (newSizeMin < oldSize) {
				// hard overflow failure - we can't even accommodate
				// new items without overflowing
				throw new ArrayIndexOutOfBoundsException("coords exceeds maximum capacity !");
			}
			// growth algorithm computation
			int grow = oldSize;
			if (grow > EXPAND_MAX_COORDS) {
				grow = Math.max(EXPAND_MAX_COORDS, oldSize >> 3); // 1/8th min
			} else if (grow < EXPAND_MIN) {
				grow = EXPAND_MIN;
			}
			assert grow > needed;

			int newSize = oldSize + grow;
			if (newSize < newSizeMin) {
				// overflow in growth algorithm computation
				newSize = Integer.MAX_VALUE;
			}
			while (true) {
				try {
					// try allocating the larger array
					return Arrays.copyOf(oldCoords, newSize);
				} catch (OutOfMemoryError oome) {
					if (newSize == newSizeMin) {
						throw oome;
					}
				}
				newSize = newSizeMin + (newSize - newSizeMin) / 2;
			}
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final synchronized void moveTo(double x, double y) {
			if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
				doubleCoords[numCoords - 2] = x;
				doubleCoords[numCoords - 1] = y;
			} else {
				needRoom(false, 2);
				pointTypes[numTypes++] = SEG_MOVETO;
				doubleCoords[numCoords++] = x;
				doubleCoords[numCoords++] = y;
			}
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final synchronized void lineTo(double x, double y) {
			needRoom(true, 2);
			pointTypes[numTypes++] = SEG_LINETO;
			doubleCoords[numCoords++] = x;
			doubleCoords[numCoords++] = y;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final synchronized void quadTo(double x1, double y1, double x2, double y2) {
			needRoom(true, 4);
			pointTypes[numTypes++] = SEG_QUADTO;
			doubleCoords[numCoords++] = x1;
			doubleCoords[numCoords++] = y1;
			doubleCoords[numCoords++] = x2;
			doubleCoords[numCoords++] = y2;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final synchronized void curveTo(double x1, double y1, double x2, double y2, double x3, double y3) {
			needRoom(true, 6);
			pointTypes[numTypes++] = SEG_CUBICTO;
			doubleCoords[numCoords++] = x1;
			doubleCoords[numCoords++] = y1;
			doubleCoords[numCoords++] = x2;
			doubleCoords[numCoords++] = y2;
			doubleCoords[numCoords++] = x3;
			doubleCoords[numCoords++] = y3;
		}

		int pointCrossings(double px, double py) {
			if (numTypes == 0) {
				return 0;
			}
			double movx, movy, curx, cury, endx, endy;
			double coords[] = doubleCoords;
			curx = movx = coords[0];
			cury = movy = coords[1];
			int crossings = 0;
			int ci = 2;
			for (int i = 1; i < numTypes; i++) {
				switch (pointTypes[i]) {
					case PathIterator.SEG_MOVETO:
						if (cury != movy) {
							crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
						}
						movx = curx = coords[ci++];
						movy = cury = coords[ci++];
						break;
					case PathIterator.SEG_LINETO:
						crossings += Curve.pointCrossingsForLine(px, py, curx, cury, endx = coords[ci++], endy = coords[ci++]);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_QUADTO:
						crossings += Curve.pointCrossingsForQuad(px, py, curx, cury, coords[ci++], coords[ci++], endx = coords[ci++], endy = coords[ci++], 0);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_CUBICTO:
						crossings += Curve.pointCrossingsForCubic(px, py, curx, cury, coords[ci++], coords[ci++], coords[ci++], coords[ci++],
								endx = coords[ci++], endy = coords[ci++], 0);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_CLOSE:
						if (cury != movy) {
							crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
						}
						curx = movx;
						cury = movy;
						break;
				}
			}
			if (cury != movy) {
				crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
			}
			return crossings;
		}

		int rectCrossings(double rxmin, double rymin, double rxmax, double rymax) {
			if (numTypes == 0) {
				return 0;
			}
			double coords[] = doubleCoords;
			double curx, cury, movx, movy, endx, endy;
			curx = movx = coords[0];
			cury = movy = coords[1];
			int crossings = 0;
			int ci = 2;
			for (int i = 1; crossings != Curve.RECT_INTERSECTS && i < numTypes; i++) {
				switch (pointTypes[i]) {
					case PathIterator.SEG_MOVETO:
						if (curx != movx || cury != movy) {
							crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
						}
						// Count should always be a multiple of 2 here.
						// assert((crossings & 1) != 0);
						movx = curx = coords[ci++];
						movy = cury = coords[ci++];
						break;
					case PathIterator.SEG_LINETO:
						endx = coords[ci++];
						endy = coords[ci++];
						crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, endx, endy);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_QUADTO:
						crossings = Curve.rectCrossingsForQuad(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[ci++], coords[ci++],
								endx = coords[ci++], endy = coords[ci++], 0);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_CUBICTO:
						crossings = Curve.rectCrossingsForCubic(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[ci++], coords[ci++], coords[ci++],
								coords[ci++], endx = coords[ci++], endy = coords[ci++], 0);
						curx = endx;
						cury = endy;
						break;
					case PathIterator.SEG_CLOSE:
						if (curx != movx || cury != movy) {
							crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
						}
						curx = movx;
						cury = movy;
						// Count should always be a multiple of 2 here.
						// assert((crossings & 1) != 0);
						break;
				}
			}
			if (crossings != Curve.RECT_INTERSECTS && (curx != movx || cury != movy)) {
				crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
			}
			// Count should always be a multiple of 2 here.
			// assert((crossings & 1) != 0);
			return crossings;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final void append(PathIterator pi, boolean connect) {
			double coords[] = new double[6];
			while (!pi.isDone()) {
				switch (pi.currentSegment(coords)) {
					case SEG_MOVETO:
						if (!connect || numTypes < 1 || numCoords < 1) {
							moveTo(coords[0], coords[1]);
							break;
						}
						if (pointTypes[numTypes - 1] != SEG_CLOSE && doubleCoords[numCoords - 2] == coords[0] && doubleCoords[numCoords - 1] == coords[1]) {
							// Collapse out initial moveto/lineto
							break;
						}
						lineTo(coords[0], coords[1]);
						break;
					case SEG_LINETO:
						lineTo(coords[0], coords[1]);
						break;
					case SEG_QUADTO:
						quadTo(coords[0], coords[1], coords[2], coords[3]);
						break;
					case SEG_CUBICTO:
						curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
						break;
					case SEG_CLOSE:
						closePath();
						break;
				}
				pi.next();
				connect = false;
			}
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final void transform(AffineTransform at) {
			at.transform(doubleCoords, 0, doubleCoords, 0, numCoords / 2);
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.6
		 */
		public final synchronized Rectangle2D getBounds2D() {
			double x1, y1, x2, y2;
			int i = numCoords;
			if (i > 0) {
				y1 = y2 = doubleCoords[--i];
				x1 = x2 = doubleCoords[--i];
				while (i > 0) {
					double y = doubleCoords[--i];
					double x = doubleCoords[--i];
					if (x < x1)
						x1 = x;
					if (y < y1)
						y1 = y;
					if (x > x2)
						x2 = x;
					if (y > y2)
						y2 = y;
				}
			} else {
				x1 = y1 = x2 = y2 = 0.0;
			}
			return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
		}

		/**
		 * {@inheritDoc}
		 * <p>
		 * The iterator for this class is not multi-threaded safe, which means that the
		 * {@code Path2D} class does not guarantee that modifications to the geometry of
		 * this {@code Path2D} object do not affect any iterations of that geometry that
		 * are already in process.
		 *
		 * @param at an {@code AffineTransform}
		 * @return a new {@code PathIterator} that iterates along the boundary of this
		 *         {@code Shape} and provides access to the geometry of this
		 *         {@code Shape}'s outline
		 * @since 1.6
		 */
		public final PathIterator getPathIterator(AffineTransform at) {
			if (at == null) {
				return new CopyIterator(this);
			} else {
				return new TxIterator(this, at);
			}
		}

		/**
		 * Creates a new object of the same class as this object.
		 *
		 * @return a clone of this instance.
		 * @exception OutOfMemoryError if there is not enough memory.
		 * @see java.lang.Cloneable
		 * @since 1.6
		 */
		public final Object clone() {
			// Note: It would be nice to have this return Path2D
			// but one of our subclasses (GeneralPath) needs to
			// offer "public Object clone()" for backwards
			// compatibility so we cannot restrict it further.
			// REMIND: Can we do both somehow?
			return new Path2D.Double(this);
		}

		/*
		 * JDK 1.6 serialVersionUID
		 */
		private static final long serialVersionUID = 1826762518450014216L;

		/**
		 * Writes the default serializable fields to the {@code ObjectOutputStream}
		 * followed by an explicit serialization of the path segments stored in this
		 * path.
		 *
		 * @serialData <a name="Path2DSerialData"><!-- --></a>
		 *             <ol>
		 *             <li>The default serializable fields. There are no default
		 *             serializable fields as of 1.6.
		 *             <li>followed by a byte indicating the storage type of the
		 *             original object as a hint (SERIAL_STORAGE_DBL_ARRAY)
		 *             <li>followed by an integer indicating the number of path segments
		 *             to follow (NP) or -1 to indicate an unknown number of path
		 *             segments follows
		 *             <li>followed by an integer indicating the total number of
		 *             coordinates to follow (NC) or -1 to indicate an unknown number of
		 *             coordinates follows (NC should always be even since coordinates
		 *             always appear in pairs representing an x,y pair)
		 *             <li>followed by a byte indicating the winding rule
		 *             ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or {@link #WIND_NON_ZERO
		 *             WIND_NON_ZERO})
		 *             <li>followed by {@code NP} (or unlimited if {@code NP < 0}) sets
		 *             of values consisting of a single byte indicating a path segment
		 *             type followed by one or more pairs of float or double values
		 *             representing the coordinates of the path segment
		 *             <li>followed by a byte indicating the end of the path
		 *             (SERIAL_PATH_END).
		 *             </ol>
		 *             <p>
		 *             The following byte value constants are used in the serialized
		 *             form of {@code Path2D} objects:
		 *             <table>
		 *             <tr>
		 *             <th>Constant Name</th>
		 *             <th>Byte Value</th>
		 *             <th>Followed by</th>
		 *             <th>Description</th>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
		 *             <td>0x30</td>
		 *             <td></td>
		 *             <td>A hint that the original {@code Path2D} object stored the
		 *             coordinates in a Java array of floats.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
		 *             <td>0x31</td>
		 *             <td></td>
		 *             <td>A hint that the original {@code Path2D} object stored the
		 *             coordinates in a Java array of doubles.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
		 *             <td>0x40</td>
		 *             <td>2 floats</td>
		 *             <td>A {@link #moveTo moveTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_FLT_LINETO}</td>
		 *             <td>0x41</td>
		 *             <td>2 floats</td>
		 *             <td>A {@link #lineTo lineTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
		 *             <td>0x42</td>
		 *             <td>4 floats</td>
		 *             <td>A {@link #quadTo quadTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
		 *             <td>0x43</td>
		 *             <td>6 floats</td>
		 *             <td>A {@link #curveTo curveTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
		 *             <td>0x50</td>
		 *             <td>2 doubles</td>
		 *             <td>A {@link #moveTo moveTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_DBL_LINETO}</td>
		 *             <td>0x51</td>
		 *             <td>2 doubles</td>
		 *             <td>A {@link #lineTo lineTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
		 *             <td>0x52</td>
		 *             <td>4 doubles</td>
		 *             <td>A {@link #curveTo curveTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
		 *             <td>0x53</td>
		 *             <td>6 doubles</td>
		 *             <td>A {@link #curveTo curveTo} path segment follows.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_SEG_CLOSE}</td>
		 *             <td>0x60</td>
		 *             <td></td>
		 *             <td>A {@link #closePath closePath} path segment.</td>
		 *             </tr>
		 *             <tr>
		 *             <td>{@code SERIAL_PATH_END}</td>
		 *             <td>0x61</td>
		 *             <td></td>
		 *             <td>There are no more path segments following.</td>
		 *             </table>
		 *
		 * @since 1.6
		 */
		private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
			super.writeObject(s, true);
		}

		/**
		 * Reads the default serializable fields from the {@code ObjectInputStream}
		 * followed by an explicit serialization of the path segments stored in this
		 * path.
		 * <p>
		 * There are no default serializable fields as of 1.6.
		 * <p>
		 * The serial data for this object is described in the writeObject method.
		 *
		 * @since 1.6
		 */
		private void readObject(java.io.ObjectInputStream s) throws java.lang.ClassNotFoundException, java.io.IOException {
			super.readObject(s, true);
		}

		static class CopyIterator extends Path2D.Iterator {
			double doubleCoords[];

			CopyIterator(Path2D.Double p2dd) {
				super(p2dd);
				this.doubleCoords = p2dd.doubleCoords;
			}

			public int currentSegment(float[] coords) {
				int type = path.pointTypes[typeIdx];
				int numCoords = curvecoords[type];
				if (numCoords > 0) {
					for (int i = 0; i < numCoords; i++) {
						coords[i] = (float) doubleCoords[pointIdx + i];
					}
				}
				return type;
			}

			public int currentSegment(double[] coords) {
				int type = path.pointTypes[typeIdx];
				int numCoords = curvecoords[type];
				if (numCoords > 0) {
					System.arraycopy(doubleCoords, pointIdx, coords, 0, numCoords);
				}
				return type;
			}
		}

		static class TxIterator extends Path2D.Iterator {
			double doubleCoords[];
			AffineTransform affine;

			TxIterator(Path2D.Double p2dd, AffineTransform at) {
				super(p2dd);
				this.doubleCoords = p2dd.doubleCoords;
				this.affine = at;
			}

			public int currentSegment(float[] coords) {
				int type = path.pointTypes[typeIdx];
				int numCoords = curvecoords[type];
				if (numCoords > 0) {
					affine.transform(doubleCoords, pointIdx, coords, 0, numCoords / 2);
				}
				return type;
			}

			public int currentSegment(double[] coords) {
				int type = path.pointTypes[typeIdx];
				int numCoords = curvecoords[type];
				if (numCoords > 0) {
					affine.transform(doubleCoords, pointIdx, coords, 0, numCoords / 2);
				}
				return type;
			}
		}
	}

	/**
	 * Adds a point to the path by moving to the specified coordinates specified in
	 * double precision.
	 *
	 * @param x the specified X coordinate
	 * @param y the specified Y coordinate
	 * @since 1.6
	 */
	public abstract void moveTo(double x, double y);

	/**
	 * Adds a point to the path by drawing a straight line from the current
	 * coordinates to the new specified coordinates specified in double precision.
	 *
	 * @param x the specified X coordinate
	 * @param y the specified Y coordinate
	 * @since 1.6
	 */
	public abstract void lineTo(double x, double y);

	/**
	 * Adds a curved segment, defined by two new points, to the path by drawing a
	 * Quadratic curve that intersects both the current coordinates and the
	 * specified coordinates {@code (x2,y2)}, using the specified point
	 * {@code (x1,y1)} as a quadratic parametric control point. All coordinates are
	 * specified in double precision.
	 *
	 * @param x1 the X coordinate of the quadratic control point
	 * @param y1 the Y coordinate of the quadratic control point
	 * @param x2 the X coordinate of the final end point
	 * @param y2 the Y coordinate of the final end point
	 * @since 1.6
	 */
	public abstract void quadTo(double x1, double y1, double x2, double y2);

	/**
	 * Adds a curved segment, defined by three new points, to the path by drawing a
	 * B&eacute;zier curve that intersects both the current coordinates and the
	 * specified coordinates {@code (x3,y3)}, using the specified points
	 * {@code (x1,y1)} and {@code (x2,y2)} as B&eacute;zier control points. All
	 * coordinates are specified in double precision.
	 *
	 * @param x1 the X coordinate of the first B&eacute;zier control point
	 * @param y1 the Y coordinate of the first B&eacute;zier control point
	 * @param x2 the X coordinate of the second B&eacute;zier control point
	 * @param y2 the Y coordinate of the second B&eacute;zier control point
	 * @param x3 the X coordinate of the final end point
	 * @param y3 the Y coordinate of the final end point
	 * @since 1.6
	 */
	public abstract void curveTo(double x1, double y1, double x2, double y2, double x3, double y3);

	/**
	 * Closes the current subpath by drawing a straight line back to the coordinates
	 * of the last {@code moveTo}. If the path is already closed then this method
	 * has no effect.
	 *
	 * @since 1.6
	 */
	public final synchronized void closePath() {
		if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
			needRoom(true, 0);
			pointTypes[numTypes++] = SEG_CLOSE;
		}
	}

	/**
	 * Appends the geometry of the specified {@code Shape} object to the path,
	 * possibly connecting the new geometry to the existing path segments with a
	 * line segment. If the {@code connect} parameter is {@code true} and the path
	 * is not empty then any initial {@code moveTo} in the geometry of the appended
	 * {@code Shape} is turned into a {@code lineTo} segment. If the destination
	 * coordinates of such a connecting {@code lineTo} segment match the ending
	 * coordinates of a currently open subpath then the segment is omitted as
	 * superfluous. The winding rule of the specified {@code Shape} is ignored and
	 * the appended geometry is governed by the winding rule specified for this
	 * path.
	 *
	 * @param s       the {@code Shape} whose geometry is appended to this path
	 * @param connect a boolean to control whether or not to turn an initial
	 *                {@code moveTo} segment into a {@code lineTo} segment to
	 *                connect the new geometry to the existing path
	 * @since 1.6
	 */
	public final void append(Shape s, boolean connect) {
		append(s.getPathIterator(null), connect);
	}

	/**
	 * Appends the geometry of the specified {@link PathIterator} object to the
	 * path, possibly connecting the new geometry to the existing path segments with
	 * a line segment. If the {@code connect} parameter is {@code true} and the path
	 * is not empty then any initial {@code moveTo} in the geometry of the appended
	 * {@code Shape} is turned into a {@code lineTo} segment. If the destination
	 * coordinates of such a connecting {@code lineTo} segment match the ending
	 * coordinates of a currently open subpath then the segment is omitted as
	 * superfluous. The winding rule of the specified {@code Shape} is ignored and
	 * the appended geometry is governed by the winding rule specified for this
	 * path.
	 *
	 * @param pi      the {@code PathIterator} whose geometry is appended to this
	 *                path
	 * @param connect a boolean to control whether or not to turn an initial
	 *                {@code moveTo} segment into a {@code lineTo} segment to
	 *                connect the new geometry to the existing path
	 * @since 1.6
	 */
	public abstract void append(PathIterator pi, boolean connect);

	/**
	 * Returns the fill style winding rule.
	 *
	 * @return an integer representing the current winding rule.
	 * @see #WIND_EVEN_ODD
	 * @see #WIND_NON_ZERO
	 * @see #setWindingRule
	 * @since 1.6
	 */
	public final synchronized int getWindingRule() {
		return windingRule;
	}

	/**
	 * Sets the winding rule for this path to the specified value.
	 *
	 * @param rule an integer representing the specified winding rule
	 * @exception IllegalArgumentException if {@code rule} is not either
	 *                                     {@link #WIND_EVEN_ODD} or
	 *                                     {@link #WIND_NON_ZERO}
	 * @see #getWindingRule
	 * @since 1.6
	 */
	public final void setWindingRule(int rule) {
		if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
			throw new IllegalArgumentException("winding rule must be " + "WIND_EVEN_ODD or " + "WIND_NON_ZERO");
		}
		windingRule = rule;
	}

	/**
	 * Returns the coordinates most recently added to the end of the path as a
	 * {@link Point2D} object.
	 *
	 * @return a {@code Point2D} object containing the ending coordinates of the
	 *         path or {@code null} if there are no points in the path.
	 * @since 1.6
	 */
	public final synchronized Point2D getCurrentPoint() {
		int index = numCoords;
		if (numTypes < 1 || index < 1) {
			return null;
		}
		if (pointTypes[numTypes - 1] == SEG_CLOSE) {
			loop: for (int i = numTypes - 2; i > 0; i--) {
				switch (pointTypes[i]) {
					case SEG_MOVETO:
						break loop;
					case SEG_LINETO:
						index -= 2;
						break;
					case SEG_QUADTO:
						index -= 4;
						break;
					case SEG_CUBICTO:
						index -= 6;
						break;
					case SEG_CLOSE:
						break;
				}
			}
		}
		return getPoint(index - 2);
	}

	/**
	 * Resets the path to empty. The append position is set back to the beginning of
	 * the path and all coordinates and point types are forgotten.
	 *
	 * @since 1.6
	 */
	public final synchronized void reset() {
		numTypes = numCoords = 0;
	}

	/**
	 * Transforms the geometry of this path using the specified
	 * {@link AffineTransform}. The geometry is transformed in place, which
	 * permanently changes the boundary defined by this object.
	 *
	 * @param at the {@code AffineTransform} used to transform the area
	 * @since 1.6
	 */
	public abstract void transform(AffineTransform at);

	/**
	 * Returns a new {@code Shape} representing a transformed version of this
	 * {@code Path2D}. Note that the exact type and coordinate precision of the
	 * return value is not specified for this method. The method will return a Shape
	 * that contains no less precision for the transformed geometry than this
	 * {@code Path2D} currently maintains, but it may contain no more precision
	 * either. If the tradeoff of precision vs. storage size in the result is
	 * important then the convenience constructors in the
	 * {@link Path2D.Float#Float(Shape, AffineTransform) Path2D.Float} and
	 * {@link Path2D.Double#Double(Shape, AffineTransform) Path2D.Double} subclasses
	 * should be used to make the choice explicit.
	 *
	 * @param at the {@code AffineTransform} used to transform a new {@code Shape}.
	 * @return a new {@code Shape}, transformed with the specified
	 *         {@code AffineTransform}.
	 * @since 1.6
	 */
	public final synchronized Shape createTransformedShape(AffineTransform at) {
		Path2D p2d = (Path2D) clone();
		if (at != null) {
			p2d.transform(at);
		}
		return p2d;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @since 1.6
	 */
	public final Rectangle getBounds() {
		return getBounds2D().getBounds();
	}

	/**
	 * Tests if the specified coordinates are inside the closed boundary of the
	 * specified {@link PathIterator}.
	 * <p>
	 * This method provides a basic facility for implementors of the {@link Shape}
	 * interface to implement support for the {@link Shape#contains(double, double)}
	 * method.
	 *
	 * @param pi the specified {@code PathIterator}
	 * @param x  the specified X coordinate
	 * @param y  the specified Y coordinate
	 * @return {@code true} if the specified coordinates are inside the specified
	 *         {@code PathIterator}; {@code false} otherwise
	 * @since 1.6
	 */
	public static boolean contains(PathIterator pi, double x, double y) {
		if (x * 0.0 + y * 0.0 == 0.0) {
			/*
			 * N * 0.0 is 0.0 only if N is finite. Here we know that both x and y are
			 * finite.
			 */
			int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 1);
			int cross = Curve.pointCrossingsForPath(pi, x, y);
			return ((cross & mask) != 0);
		} else {
			/*
			 * Either x or y was infinite or NaN. A NaN always produces a negative response
			 * to any test and Infinity values cannot be "inside" any path so they should
			 * return false as well.
			 */
			return false;
		}
	}

	/**
	 * Tests if the specified {@link Point2D} is inside the closed boundary of the
	 * specified {@link PathIterator}.
	 * <p>
	 * This method provides a basic facility for implementors of the {@link Shape}
	 * interface to implement support for the {@link Shape#contains(Point2D)}
	 * method.
	 *
	 * @param pi the specified {@code PathIterator}
	 * @param p  the specified {@code Point2D}
	 * @return {@code true} if the specified coordinates are inside the specified
	 *         {@code PathIterator}; {@code false} otherwise
	 * @since 1.6
	 */
	public static boolean contains(PathIterator pi, Point2D p) {
		return contains(pi, p.getX(), p.getY());
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @since 1.6
	 */
	public final boolean contains(double x, double y) {
		if (x * 0.0 + y * 0.0 == 0.0) {
			/*
			 * N * 0.0 is 0.0 only if N is finite. Here we know that both x and y are
			 * finite.
			 */
			if (numTypes < 2) {
				return false;
			}
			int mask = (windingRule == WIND_NON_ZERO ? -1 : 1);
			return ((pointCrossings(x, y) & mask) != 0);
		} else {
			/*
			 * Either x or y was infinite or NaN. A NaN always produces a negative response
			 * to any test and Infinity values cannot be "inside" any path so they should
			 * return false as well.
			 */
			return false;
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @since 1.6
	 */
	public final boolean contains(Point2D p) {
		return contains(p.getX(), p.getY());
	}

	/**
	 * Tests if the specified rectangular area is entirely inside the closed
	 * boundary of the specified {@link PathIterator}.
	 * <p>
	 * This method provides a basic facility for implementors of the {@link Shape}
	 * interface to implement support for the
	 * {@link Shape#contains(double, double, double, double)} method.
	 * <p>
	 * This method object may conservatively return false in cases where the
	 * specified rectangular area intersects a segment of the path, but that segment
	 * does not represent a boundary between the interior and exterior of the path.
	 * Such segments could lie entirely within the interior of the path if they are
	 * part of a path with a {@link #WIND_NON_ZERO} winding rule or if the segments
	 * are retraced in the reverse direction such that the two sets of segments
	 * cancel each other out without any exterior area falling between them. To
	 * determine whether segments represent true boundaries of the interior of the
	 * path would require extensive calculations involving all of the segments of
	 * the path and the winding rule and are thus beyond the scope of this
	 * implementation.
	 *
	 * @param pi the specified {@code PathIterator}
	 * @param x  the specified X coordinate
	 * @param y  the specified Y coordinate
	 * @param w  the width of the specified rectangular area
	 * @param h  the height of the specified rectangular area
	 * @return {@code true} if the specified {@code PathIterator} contains the
	 *         specified rectangular area; {@code false} otherwise.
	 * @since 1.6
	 */
	public static boolean contains(PathIterator pi, double x, double y, double w, double h) {
		if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
			/*
			 * [xy]+[wh] is NaN if any of those values are NaN, or if adding the two
			 * together would produce NaN by virtue of adding opposing Infinte values. Since
			 * we need to add them below, their sum must not be NaN. We return false because
			 * NaN always produces a negative response to tests
			 */
			return false;
		}
		if (w <= 0 || h <= 0) {
			return false;
		}
		int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
		int crossings = Curve.rectCrossingsForPath(pi, x, y, x + w, y + h);
		return (crossings != Curve.RECT_INTERSECTS && (crossings & mask) != 0);
	}

	/**
	 * Tests if the specified {@link Rectangle2D} is entirely inside the closed
	 * boundary of the specified {@link PathIterator}.
	 * <p>
	 * This method provides a basic facility for implementors of the {@link Shape}
	 * interface to implement support for the {@link Shape#contains(Rectangle2D)}
	 * method.
	 * <p>
	 * This method object may conservatively return false in cases where the
	 * specified rectangular area intersects a segment of the path, but that segment
	 * does not represent a boundary between the interior and exterior of the path.
	 * Such segments could lie entirely within the interior of the path if they are
	 * part of a path with a {@link #WIND_NON_ZERO} winding rule or if the segments
	 * are retraced in the reverse direction such that the two sets of segments
	 * cancel each other out without any exterior area falling between them. To
	 * determine whether segments represent true boundaries of the interior of the
	 * path would require extensive calculations involving all of the segments of
	 * the path and the winding rule and are thus beyond the scope of this
	 * implementation.
	 *
	 * @param pi the specified {@code PathIterator}
	 * @param r  a specified {@code Rectangle2D}
	 * @return {@code true} if the specified {@code PathIterator} contains the
	 *         specified {@code Rectangle2D}; {@code false} otherwise.
	 * @since 1.6
	 */
	public static boolean contains(PathIterator pi, Rectangle2D r) {
		return contains(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * This method object may conservatively return false in cases where the
	 * specified rectangular area intersects a segment of the path, but that segment
	 * does not represent a boundary between the interior and exterior of the path.
	 * Such segments could lie entirely within the interior of the path if they are
	 * part of a path with a {@link #WIND_NON_ZERO} winding rule or if the segments
	 * are retraced in the reverse direction such that the two sets of segments
	 * cancel each other out without any exterior area falling between them. To
	 * determine whether segments represent true boundaries of the interior of the
	 * path would require extensive calculations involving all of the segments of
	 * the path and the winding rule and are thus beyond the scope of this
	 * implementation.
	 *
	 * @since 1.6
	 */
	public final boolean contains(double x, double y, double w, double h) {
		if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
			/*
			 * [xy]+[wh] is NaN if any of those values are NaN, or if adding the two
			 * together would produce NaN by virtue of adding opposing Infinte values. Since
			 * we need to add them below, their sum must not be NaN. We return false because
			 * NaN always produces a negative response to tests
			 */
			return false;
		}
		if (w <= 0 || h <= 0) {
			return false;
		}
		int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
		int crossings = rectCrossings(x, y, x + w, y + h);
		return (crossings != Curve.RECT_INTERSECTS && (crossings & mask) != 0);
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * This method object may conservatively return false in cases where the
	 * specified rectangular area intersects a segment of the path, but that segment
	 * does not represent a boundary between the interior and exterior of the path.
	 * Such segments could lie entirely within the interior of the path if they are
	 * part of a path with a {@link #WIND_NON_ZERO} winding rule or if the segments
	 * are retraced in the reverse direction such that the two sets of segments
	 * cancel each other out without any exterior area falling between them. To
	 * determine whether segments represent true boundaries of the interior of the
	 * path would require extensive calculations involving all of the segments of
	 * the path and the winding rule and are thus beyond the scope of this
	 * implementation.
	 *
	 * @since 1.6
	 */
	public final boolean contains(Rectangle2D r) {
		return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
	}

	/**
	 * Tests if the interior of the specified {@link PathIterator} intersects the
	 * interior of a specified set of rectangular coordinates.
	 * <p>
	 * This method provides a basic facility for implementors of the {@link Shape}
	 * interface to implement support for the
	 * {@link Shape#intersects(double, double, double, double)} method.
	 * <p>
	 * This method object may conservatively return true in cases where the
	 * specified rectangular area intersects a segment of the path, but that segment
	 * does not represent a boundary between the interior and exterior of the path.
	 * Such a case may occur if some set of segments of the path are retraced in the
	 * reverse direction such that the two sets of segments cancel each other out
	 * without any interior area between them. To determine whether segments
	 * represent true boundaries of the interior of the path would require extensive
	 * calculations involving all of the segments of the path and the winding rule
	 * and are thus beyond the scope of this implementation.
	 *
	 * @param pi the specified {@code PathIterator}
	 * @param x  the specified X coordinate
	 * @param y  the specified Y coordinate
	 * @param w  the width of the specified rectangular coordinates
	 * @param h  the height of the specified rectangular coordinates
	 * @return {@code true} if the specified {@code PathIterator} and the interior
	 *         of the specified set of rectangular coordinates intersect each other;
	 *         {@code false} otherwise.
	 * @since 1.6
	 */
	public static boolean intersects(PathIterator pi, double x, double y, double w, double h) {
		if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
			/*
			 * [xy]+[wh] is NaN if any of those values are NaN, or if adding the two
			 * together would produce NaN by virtue of adding opposing Infinte values. Since
			 * we need to add them below, their sum must not be NaN. We return false because
			 * NaN always produces a negative response to tests
			 */
			return false;
		}
		if (w <= 0 || h <= 0) {
			return false;
		}
		int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
		int crossings = Curve.rectCrossingsForPath(pi, x, y, x + w, y + h);
		return (crossings == Curve.RECT_INTERSECTS || (crossings & mask) != 0);
	}

	/**
	 * Tests if the interior of the specified {@link PathIterator} intersects the
	 * interior of a specified {@link Rectangle2D}.
	 * <p>
	 * This method provides a basic facility for implementors of the {@link Shape}
	 * interface to implement support for the {@link Shape#intersects(Rectangle2D)}
	 * method.
	 * <p>
	 * This method object may conservatively return true in cases where the
	 * specified rectangular area intersects a segment of the path, but that segment
	 * does not represent a boundary between the interior and exterior of the path.
	 * Such a case may occur if some set of segments of the path are retraced in the
	 * reverse direction such that the two sets of segments cancel each other out
	 * without any interior area between them. To determine whether segments
	 * represent true boundaries of the interior of the path would require extensive
	 * calculations involving all of the segments of the path and the winding rule
	 * and are thus beyond the scope of this implementation.
	 *
	 * @param pi the specified {@code PathIterator}
	 * @param r  the specified {@code Rectangle2D}
	 * @return {@code true} if the specified {@code PathIterator} and the interior
	 *         of the specified {@code Rectangle2D} intersect each other;
	 *         {@code false} otherwise.
	 * @since 1.6
	 */
	public static boolean intersects(PathIterator pi, Rectangle2D r) {
		return intersects(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * This method object may conservatively return true in cases where the
	 * specified rectangular area intersects a segment of the path, but that segment
	 * does not represent a boundary between the interior and exterior of the path.
	 * Such a case may occur if some set of segments of the path are retraced in the
	 * reverse direction such that the two sets of segments cancel each other out
	 * without any interior area between them. To determine whether segments
	 * represent true boundaries of the interior of the path would require extensive
	 * calculations involving all of the segments of the path and the winding rule
	 * and are thus beyond the scope of this implementation.
	 *
	 * @since 1.6
	 */
	public final boolean intersects(double x, double y, double w, double h) {
		if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
			/*
			 * [xy]+[wh] is NaN if any of those values are NaN, or if adding the two
			 * together would produce NaN by virtue of adding opposing Infinte values. Since
			 * we need to add them below, their sum must not be NaN. We return false because
			 * NaN always produces a negative response to tests
			 */
			return false;
		}
		if (w <= 0 || h <= 0) {
			return false;
		}
		int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
		int crossings = rectCrossings(x, y, x + w, y + h);
		return (crossings == Curve.RECT_INTERSECTS || (crossings & mask) != 0);
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * This method object may conservatively return true in cases where the
	 * specified rectangular area intersects a segment of the path, but that segment
	 * does not represent a boundary between the interior and exterior of the path.
	 * Such a case may occur if some set of segments of the path are retraced in the
	 * reverse direction such that the two sets of segments cancel each other out
	 * without any interior area between them. To determine whether segments
	 * represent true boundaries of the interior of the path would require extensive
	 * calculations involving all of the segments of the path and the winding rule
	 * and are thus beyond the scope of this implementation.
	 *
	 * @since 1.6
	 */
	public final boolean intersects(Rectangle2D r) {
		return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * The iterator for this class is not multi-threaded safe, which means that this
	 * {@code Path2D} class does not guarantee that modifications to the geometry of
	 * this {@code Path2D} object do not affect any iterations of that geometry that
	 * are already in process.
	 *
	 * @since 1.6
	 */
	public final PathIterator getPathIterator(AffineTransform at, double flatness) {
		return new FlatteningPathIterator(getPathIterator(at), flatness);
	}

	/**
	 * Creates a new object of the same class as this object.
	 *
	 * @return a clone of this instance.
	 * @exception OutOfMemoryError if there is not enough memory.
	 * @see java.lang.Cloneable
	 * @since 1.6
	 */
	public abstract Object clone();
	// Note: It would be nice to have this return Path2D
	// but one of our subclasses (GeneralPath) needs to
	// offer "public Object clone()" for backwards
	// compatibility so we cannot restrict it further.
	// REMIND: Can we do both somehow?

	/*
	 * Support fields and methods for serializing the subclasses.
	 */
	private static final byte SERIAL_STORAGE_FLT_ARRAY = 0x30;
	private static final byte SERIAL_STORAGE_DBL_ARRAY = 0x31;

	private static final byte SERIAL_SEG_FLT_MOVETO = 0x40;
	private static final byte SERIAL_SEG_FLT_LINETO = 0x41;
	private static final byte SERIAL_SEG_FLT_QUADTO = 0x42;
	private static final byte SERIAL_SEG_FLT_CUBICTO = 0x43;

	private static final byte SERIAL_SEG_DBL_MOVETO = 0x50;
	private static final byte SERIAL_SEG_DBL_LINETO = 0x51;
	private static final byte SERIAL_SEG_DBL_QUADTO = 0x52;
	private static final byte SERIAL_SEG_DBL_CUBICTO = 0x53;

	private static final byte SERIAL_SEG_CLOSE = 0x60;
	private static final byte SERIAL_PATH_END = 0x61;

	final void writeObject(java.io.ObjectOutputStream s, boolean isdbl) throws java.io.IOException {
		s.defaultWriteObject();

		float fCoords[];
		double dCoords[];

		if (isdbl) {
			dCoords = ((Path2D.Double) this).doubleCoords;
			fCoords = null;
		} else {
			fCoords = ((Path2D.Float) this).floatCoords;
			dCoords = null;
		}

		int numTypes = this.numTypes;

		s.writeByte(isdbl ? SERIAL_STORAGE_DBL_ARRAY : SERIAL_STORAGE_FLT_ARRAY);
		s.writeInt(numTypes);
		s.writeInt(numCoords);
		s.writeByte((byte) windingRule);

		int cindex = 0;
		for (int i = 0; i < numTypes; i++) {
			int npoints;
			byte serialtype;
			switch (pointTypes[i]) {
				case SEG_MOVETO:
					npoints = 1;
					serialtype = (isdbl ? SERIAL_SEG_DBL_MOVETO : SERIAL_SEG_FLT_MOVETO);
					break;
				case SEG_LINETO:
					npoints = 1;
					serialtype = (isdbl ? SERIAL_SEG_DBL_LINETO : SERIAL_SEG_FLT_LINETO);
					break;
				case SEG_QUADTO:
					npoints = 2;
					serialtype = (isdbl ? SERIAL_SEG_DBL_QUADTO : SERIAL_SEG_FLT_QUADTO);
					break;
				case SEG_CUBICTO:
					npoints = 3;
					serialtype = (isdbl ? SERIAL_SEG_DBL_CUBICTO : SERIAL_SEG_FLT_CUBICTO);
					break;
				case SEG_CLOSE:
					npoints = 0;
					serialtype = SERIAL_SEG_CLOSE;
					break;

				default:
					// Should never happen
					throw new InternalError("unrecognized path type");
			}
			s.writeByte(serialtype);
			while (--npoints >= 0) {
				if (isdbl) {
					s.writeDouble(dCoords[cindex++]);
					s.writeDouble(dCoords[cindex++]);
				} else {
					s.writeFloat(fCoords[cindex++]);
					s.writeFloat(fCoords[cindex++]);
				}
			}
		}
		s.writeByte(SERIAL_PATH_END);
	}

	final void readObject(java.io.ObjectInputStream s, boolean storedbl) throws java.lang.ClassNotFoundException, java.io.IOException {
		s.defaultReadObject();

		// The subclass calls this method with the storage type that
		// they want us to use (storedbl) so we ignore the storage
		// method hint from the stream.
		s.readByte();
		int nT = s.readInt();
		int nC = s.readInt();
		try {
			setWindingRule(s.readByte());
		} catch (IllegalArgumentException iae) {
			throw new java.io.InvalidObjectException(iae.getMessage());
		}

		pointTypes = new byte[(nT < 0) ? INIT_SIZE : nT];
		if (nC < 0) {
			nC = INIT_SIZE * 2;
		}
		if (storedbl) {
			((Path2D.Double) this).doubleCoords = new double[nC];
		} else {
			((Path2D.Float) this).floatCoords = new float[nC];
		}

		PATHDONE: for (int i = 0; nT < 0 || i < nT; i++) {
			boolean isdbl;
			int npoints;
			byte segtype;

			byte serialtype = s.readByte();
			switch (serialtype) {
				case SERIAL_SEG_FLT_MOVETO:
					isdbl = false;
					npoints = 1;
					segtype = SEG_MOVETO;
					break;
				case SERIAL_SEG_FLT_LINETO:
					isdbl = false;
					npoints = 1;
					segtype = SEG_LINETO;
					break;
				case SERIAL_SEG_FLT_QUADTO:
					isdbl = false;
					npoints = 2;
					segtype = SEG_QUADTO;
					break;
				case SERIAL_SEG_FLT_CUBICTO:
					isdbl = false;
					npoints = 3;
					segtype = SEG_CUBICTO;
					break;

				case SERIAL_SEG_DBL_MOVETO:
					isdbl = true;
					npoints = 1;
					segtype = SEG_MOVETO;
					break;
				case SERIAL_SEG_DBL_LINETO:
					isdbl = true;
					npoints = 1;
					segtype = SEG_LINETO;
					break;
				case SERIAL_SEG_DBL_QUADTO:
					isdbl = true;
					npoints = 2;
					segtype = SEG_QUADTO;
					break;
				case SERIAL_SEG_DBL_CUBICTO:
					isdbl = true;
					npoints = 3;
					segtype = SEG_CUBICTO;
					break;

				case SERIAL_SEG_CLOSE:
					isdbl = false;
					npoints = 0;
					segtype = SEG_CLOSE;
					break;

				case SERIAL_PATH_END:
					if (nT < 0) {
						break PATHDONE;
					}
					throw new StreamCorruptedException("unexpected PATH_END");

				default:
					throw new StreamCorruptedException("unrecognized path type");
			}
			needRoom(segtype != SEG_MOVETO, npoints * 2);
			if (isdbl) {
				while (--npoints >= 0) {
					append(s.readDouble(), s.readDouble());
				}
			} else {
				while (--npoints >= 0) {
					append(s.readFloat(), s.readFloat());
				}
			}
			pointTypes[numTypes++] = segtype;
		}
		if (nT >= 0 && s.readByte() != SERIAL_PATH_END) {
			throw new StreamCorruptedException("missing PATH_END");
		}
	}

	static abstract class Iterator implements PathIterator {
		int typeIdx;
		int pointIdx;
		Path2D path;

		static final int curvecoords[] =
			{ 2, 2, 4, 6, 0 };

		Iterator(Path2D path) {
			this.path = path;
		}

		public int getWindingRule() {
			return path.getWindingRule();
		}

		public boolean isDone() {
			return (typeIdx >= path.numTypes);
		}

		public void next() {
			int type = path.pointTypes[typeIdx++];
			pointIdx += curvecoords[type];
		}
	}
}
